#ifndef __Plane_hpp__
#define __Plane_hpp__

#include "Vector3.hpp"

namespace ge {

//----------------------------------------------------------------------------//
// Plane
//----------------------------------------------------------------------------//

class Plane
{
 public:

     Plane( void ) : normal( Vector3::Zero ), distance( 0.0f )
     {
     }

     Plane& Set( float _nx, float _ny, float _nz, float _d )
     {
          normal.Set( _nx, _ny, _nz );
          distance = _d;
          return *this;
     }

     Plane& Set( const Vector3& _normal, float _distance )
     {
          normal = _normal;
          distance = _distance;
          return *this;
     }

     Plane& FromPoint( const Vector3& _normal, const Vector3& _point )
     {
          normal = _normal;    
          distance = -normal.Dot( _point );
          return *this;
     }

     Plane& Normalize( void )
     {
          float _length;
          normal.SafeNormalize( _length );
          if( _length > Math::EpsilonSq ) distance /= _length;
     }

     enum Side
     {
          Negative = -1,
          Both = 0,
          Positive = 1,
     };

     float Distance( const Vector3& _point ) const
     {
          return Distance( normal, distance, _point );
     }

     int ClassifyPointRelation( const Vector3& _point, float* _distance = 0 ) const
     {
          return ClassifyPointRelation( normal, distance, _point, _distance );
     }

     int ClassifySphereRelation( const Vector3& _center, float _radius, float* _distance = 0 ) const
     {
          return ClassifySphereRelation( normal, distance, _center, _radius, _distance );
     }

     static float Distance( const Vector3& _plane_normal, float _plane_distance, const Vector3& _point )
     {
          return _plane_normal.Dot( _point ) + _plane_distance;
     }

     static int ClassifyPointRelation( const Vector3& _plane_normal, float _plane_distance, const Vector3& _point, float* _distance = 0 )
     {
          float _d = Distance( _plane_normal, _plane_distance, _point );
          if( _distance ) *_distance = _d;
          if( _d < 0.0f ) return Plane::Negative;
          if( _d > 0.0f ) return Plane::Positive;
          return Plane::Both;
     }

     static int ClassifySphereRelation( const Vector3& _plane_normal, float _plane_distance, const Vector3& _sphere_center, float _sphere_radius, float* _distance = 0 )
     {
          float _d = Distance( _plane_normal, _plane_distance, _sphere_center );
          float _md = _plane_normal.AbsDot( _sphere_radius );
          if( _distance ) *_distance = _d;
          if( _d < -_md ) return Plane::Negative;
          if( _d > _md ) return Plane::Positive;
          return Plane::Both;
     }

 public:

     Vector3 normal;
     float distance;
};

} // namespace ge

#endif//__Plane_hpp__
